library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;


entity fsm is
generic( opSize  : integer := 6;
			regSize : integer := 5);
port( clk		 : in  std_logic;
		rst		 : in  std_logic;
		opcode	 : in  std_logic_vector (opSize-1 downto 0);	-- opcode form the IR
		r_t 		 : in  std_logic_vector (regSize-1 downto 0);
		r_d 		 : in  std_logic_vector (regSize-1 downto 0);
		func		 : in  std_logic_vector (opSize-1 downto 0);	-- function from the IR
		Rd			 : out std_logic_vector (regSize-1 downto 0);-- destination register
		alu_mux   : out std_logic_vector (3 downto 0);			-- operation sent to the ALU
		info 		 : out std_logic;										-- signed/unsigned, positive/negative, left/right
		types		 : out std_logic;										-- addition/subtraction, logical/arithmetical
		extra		 : out std_logic;										-- shift/rotate, logical-op
		left_mux  : out std_logic;  									-- left muxer pilot
		right_mux : out std_logic; 									-- right muxer pilot
		cond_pilot: out std_logic_vector (2 downto 0);			-- condition block pilot
		mem_cnt   : out std_logic_vector (1 downto 0);			-- datamem module control signal
		mem_wr	 : out std_logic;										-- read or write in the datamem
		npc_mux   : out std_logic;										-- mem mux_1 pilot
		w_en		 : out std_logic;										-- write enable for the register file
		wb_mux 	 : out std_logic;										-- writeback muxer pilot	
		en_if 	 : out std_logic;
		en_id 	 : out std_logic;
		en_ex 	 : out std_logic;
		en_mm 	 : out std_logic
);
end fsm;

architecture Behavioral of fsm is
signal en 	 : std_logic_vector(2 downto 0);
begin

ENBL: process (clk,rst)
begin
		
	if rst='1' then
		en 	<= "000";
		en_if <= '0';
		en_id <= '0';
		en_ex <= '0';
		en_mm <= '0';
	elsif rising_edge(clk) then
		case en is
			when "000" =>
				en_if <= '1';
				en_id <= '0';
				en_ex <= '0';
				en_mm <= '0';

				en <= "001";
			when "001" =>
				en_if <= '0';
				en_id <= '1';
				en_ex <= '0';
				en_mm <= '0';

				en <= "011";
			when "011" =>
				en_if <= '0';
				en_id <= '0';
				en_ex <= '1';
				en_mm <= '0';
				
				en <= "010";
			when "010" =>
				en_if <= '0';
				en_id <= '0';
				en_ex <= '0';
				en_mm <= '1';
				
				en <= "110";
			when "110" =>
				en_if <= '0';
				en_id <= '0';
				en_ex <= '0';
				en_mm <= '0';
				
				en <= "000";
			when others =>
				en_if <= '0';
				en_id <= '0';
				en_ex <= '0';
				en_mm <= '0';

				en <= "000";
		end case;
	end if;
end process ENBL;

REGIST: process (opcode,r_t,r_d)
begin

	case opcode(5 downto 0) is
		when "010100" | "010101" => 	-- writes on REGS[31]
			Rd(4 downto 0) <= "11111"; 
		when "001100" => 					-- OP_ALU
			Rd(4 downto 0) <= r_d(4 downto 0);
		when others =>						-- otherwise use the second register (B)
			Rd(4 downto 0) <= r_t(4 downto 0);
	end case;
	
end process REGIST;

DECODE: process (opcode,func)
begin

--default cases
wb_mux		<= '1';			-- memory or alu output
npc_mux		<= '0';			-- alu out selected
w_en			<= '1';			-- enable register file
mem_cnt		<= "00";			-- memory output disabled
mem_wr		<= '0';			-- memory read
cond_pilot	<= "000";		-- condition always false	
info			<= '0';
extra			<= '0';
types			<= '0';
case opcode(5 downto 0) is
	when "000001" => -- OP_ADDI (add signed immediate)
		left_mux		<= '0';			-- register A
		right_mux	<= '1';			-- immediate
		alu_mux		<= "0001";		-- ADD_SUB
		info			<= '1';			-- signed number
		types			<= '0';			-- addition
	when "000010" => -- OP_ADDUI (add unsigned immediate)
		left_mux		<= '0';			-- register A
		right_mux	<= '1';			-- immediate
		alu_mux		<= "0001";		-- ADD_SUB
		info			<= '0';			-- unsigned	number
		types			<= '0';			-- addition
	when "000011" => -- OP_SUBI (sub signed immediate)
		left_mux		<= '0';			-- register A
		right_mux	<= '1';			-- immediate
		alu_mux		<= "0001";		-- ADD_SUB
		info			<= '1';			-- signed number
		types			<= '1';			-- subtraction	
	when "000100" => -- OP_SUBUI (sub unsigned immediate)
		left_mux		<= '0';			-- register A
		right_mux	<= '1';			-- immediate
		alu_mux		<= "0001";		-- ADD_SUB
		info			<= '0';			-- unsigned number
		types			<= '1';			-- subtraction	
	when "000101" => -- OP_MULI (mul signed immediate)
		left_mux		<= '0';			-- register A
		right_mux	<= '1';			-- immediate
		alu_mux		<= "0010";		-- MUL
		info			<= '1';			-- signed number
	when "000110" => -- OP_MULUI (mul unsigned immediate)
		left_mux 	<= '0';			-- register A
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0010";		-- MUL
		info		 	<= '0';			-- unsigned number
	when "000111" => -- OP_DIV
		left_mux  	<= '0';			-- register A
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0011";		-- DIV
		info		 	<= '1';			-- signed number
	when "001000" => -- OP_DIUV
		left_mux  	<= '0';			-- register A
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0011";		-- DIV
		info		 	<= '0';			-- unsigned number
	when "001001" => -- OP_ANDI ( & immediate)
		left_mux  	<= '0';			-- register A
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0100";		-- LOG
		info		 	<= '0';			-- positive operation
		types		 	<= '0';
		extra		 	<= '0';
	when "001010" => -- OP_ORI ( | immediate)
		left_mux  	<= '0';			-- register A
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0100";		-- LOG
		info		 	<= '0';			-- positive operation
		types		 	<= '0';
		extra		 	<= '1';
	when "001011" => -- OP_XORI ( xor immediate)
		left_mux  	<= '0';			-- register A
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0100";		-- LOG
		info		 	<= '0';			-- positive operation
		types		 	<= '1';
		extra		 	<= '0';
	when "001100" => -- OP_ALU (aggregate ALU operations opcode)
		left_mux 	<= '0';			-- register A
		right_mux 	<= '0';			-- register B
		w_en 			<= '1';			-- enables data to be saved in the register file
		case func(5 downto 0) is
			when "000001" => -- FUNC_ADD (signed add)
				alu_mux	 <= "0001";		-- ADD_SUB
				info		 <= '1';			-- signed number
				types		 <= '0';			-- addition
			when "000010" => -- FUNC_ADDU (unsigned add)
				alu_mux	 <= "0001";		-- ADD_SUB
				info		 <= '0';			-- unsigned	number
				types		 <= '0';			-- addition		
			when "000011" => -- FUNC_SUB (signed sub)
				alu_mux	 <= "0001";		-- ADD_SUB
				info		 <= '1';			-- signed number
				types		 <= '1';			-- subtraction	
			when "000100" => -- FUNC_SUBU (unsigned sub)
				alu_mux	 <= "0001";		-- ADD_SUB
				info		 <= '0';			-- unsigned number
				types		 <= '1';			-- subtraction	
			when "000101" => -- FUNC_MUL (signed mul)
				alu_mux	 <= "0010";		-- MUL
				info		 <= '1';			-- signed number
			when "000110" => -- FUNC_MULU (unsigned mul)
				alu_mux	 <= "0010";		-- MUL
				info		 <= '0';			-- unsigned number
			when "000111" => -- FUNC_DIV
				alu_mux	 <= "0011";		-- DIV
				info		 <= '1';			-- signed number
			when "001000" => -- FUNC_DIVU
				alu_mux	 <= "0011";		-- DIV
				info		 <= '0';			-- unsigned number
			when "001001" => -- FUNC_LLS (logical left shift)
				alu_mux	 <= "1000";		-- SHIFT
				info		 <= '0';			-- left
				types		 <= '0';			-- logical shift
				extra		 <= '0';			-- shift
			when "001010" => -- FUNC_LRS (logical right shift)
				alu_mux	 <= "1000";		-- SHIFT
				info		 <= '1';			-- right
				types		 <= '0';			-- logical shift
				extra		 <= '0';			-- shift
			when "001011" => -- FUNC_ALS (arithmetical left shift)	
				alu_mux	 <= "1000";		-- SHIFT
				info		 <= '0';			-- left
				types		 <= '1';			-- arithmetical shift
				extra		 <= '0';			-- shift
			when "001100" => -- FUNC_ARS (arithmetical right shift)
				alu_mux	 <= "1000";		-- SHIFT
				info		 <= '1';			-- right
				types		 <= '1';			-- arithmetical shift
				extra		 <= '0';			-- shift
			when "001101" => -- FUNC_LR (logical left rotate)
				alu_mux	 <= "1000";		-- SHIFT
				info		 <= '0';			-- left
				extra		 <= '1';			-- rotate
			when "001110" => -- FUNC_RR (logical left rotate)
				alu_mux	 <= "1000";		-- SHIFT
				info		 <= '1';			-- right
				extra		 <= '1';			-- rotate			
			when "001111" => -- FUNC_AND (bitwise and)
				alu_mux	 <= "0100";		-- LOG
				info		 <= '0';			-- positive operation
				types		 <= '0';
				extra		 <= '0';
			when "010000" => -- FUNC_OR (bitwise or)
				alu_mux	 <= "0100";		-- LOG
				info		 <= '0';			-- positive operation
				types		 <= '0';
				extra		 <= '1';
			when "010001" => -- FUNC_XOR (bitwise xor)
				alu_mux	 <= "0100";		-- LOG
				info		 <= '0';			-- positive operation
				types		 <= '1';
				extra		 <= '0';
			-- additional operations
			when "010010" => -- FUNC_MOV (move registers)
				alu_mux	 <= "1010";
			when "010011" => -- FUNC_NAND (bitwise nand)
				alu_mux	 <= "0100";		-- LOG
				info		 <= '1';			-- negative operation
				types		 <= '0';
				extra		 <= '0';
			when "010100" => -- FUNC_NOR (bitwise nor)
				alu_mux	 <= "0100";		-- LOG
				info		 <= '1';			-- negative operation
				types		 <= '0';
				extra		 <= '1';
			when "010101" => -- FUNC_XNOR (bitwise xnor)
				alu_mux	 <= "0100";		-- LOG
				info		 <= '1';			-- negative operation
				types		 <= '1';
				extra		 <= '0';
			when "010110" => -- FUNC_MOD
				alu_mux	 	<= "1001";		-- MOD
				info		 	<= '1';			-- signed number
			when "010111" => -- FUNC_MODU
				alu_mux	 	<= "1001";		-- MOD
				info		 	<= '0';			-- unsigned number
			when others =>
				alu_mux	 <= "0000";
		end case;
	when "001101" => --OP_LW (load 32-bits word)
		left_mux		<= '0';			-- register A
		right_mux	<= '1';			-- immediate
		alu_mux		<= "0001";		-- ADD_SUB
		info			<= '0';			-- unsigned	immediate
		types			<= '0';			-- addition	
		wb_mux 		<= '0';			-- memory out
		mem_cnt 		<= "11";			-- load 32 bits
	when "001110" => --OP_LH (load 16-bits half-word)
		left_mux  	<= '0';			-- register A
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0001";		-- ADD_SUB
		info		 	<= '0';			-- unsigned	immediate
		types	 		<= '0';			-- addition	
		wb_mux 		<= '0';			-- memory out
		mem_cnt 	 	<= "10";			-- load 16 bits
	when "001111" => --OP_LB (load byte)
		left_mux  	<= '0';			-- register A
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0001";		-- ADD_SUB
		info		 	<= '0';			-- unsigned	immediate
		types	 		<= '0';			-- addition	
		wb_mux 	 	<= '0';			-- memory out
		mem_cnt 	 	<= "01";			-- load 8 bits
	when "010000" => --OP_SW (store 32-bits word)
		left_mux 	<= '0';			-- register A
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0001";		-- ADD_SUB
		info		 	<= '0';			-- unsigned	immediate
		types		 	<= '0';			-- addition	
		mem_cnt 	 	<= "11";			-- load 32 bits
		w_en 		 	<= '0';			-- disable the register file
		mem_wr		<= '1';			-- memory write
	when "010001" => --OP_SH (store 16-bits half-word)
		left_mux		<= '0';			-- register A
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0001";		-- ADD_SUB
		info		 	<= '0';			-- unsigned	immediate
		types		 	<= '0';			-- addition	
		mem_cnt 	 	<= "10";			-- load 16 bits
		w_en 		 	<= '0';			-- disable the register file
		mem_wr		<= '1';			-- memory write
	when "010010" => --OP_SB (load byte)
		left_mux		<= '0';			-- register A
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0001";		-- ADD_SUB
		info		 	<= '0';			-- unsigned	number
		types		 	<= '0';			-- addition	
		mem_cnt 	 	<= "01";			-- load 8 bits
		w_en 		 	<= '0';			-- disable the register file
		mem_wr		<= '1';			-- memory write
	when "010011" => --OP_J (jump to immediate)
		left_mux  	<= '1'; 			-- NPC
		right_mux 	<= '1'; 			-- immediate
		alu_mux	 	<= "0101";		-- shift a constant value
		cond_pilot	<= "111"; 		-- always true
		npc_mux 	 	<= '0';			-- don't care about REGS[31]
		w_en 		 	<= '0';			-- disable the register file
	when "010100" => --OP_JAL (jump and link)
		left_mux  	<= '1'; 			-- NPC
		right_mux 	<= '1'; 			-- immediate
		alu_mux	 	<= "0101";		-- shift a constant value
		cond_pilot	<= "111"; 		-- always true
		npc_mux 	 	<= '1';			-- save NPC in REGS[31]
		w_en 		 	<= '1';			-- enable the register file
	when "010101" => --OP_JALR (jump and link register)
		left_mux  	<= '1'; 			-- NPC
		right_mux 	<= '1'; 			-- immediate
		alu_mux	 	<= "0110";		-- connection to register
		cond_pilot	<= "111"; 		-- always true
		npc_mux 	 	<= '1';			-- save NPC in REGS[31]
		w_en 		 	<= '1';			-- enable the register file
	when "010110" => --OP_JR (jump to register)
		left_mux  	<= '1'; 			-- NPC
		right_mux 	<= '1'; 			-- immediate
		alu_mux	 	<= "0110";		-- connection to register
		cond_pilot	<= "111"; 		-- always true
		npc_mux 	 	<= '0';			-- don't care abo
		right_mux 	<= '1';			-- immediateut REGS[31]
		w_en 		 	<= '0';			-- disable the register file
	when "010111" => --OP_BEQZ (branch equal zero)
		left_mux  	<= '1';			-- NPC
		right_mux 	<= '1'; 			-- immediate
		alu_mux	 	<= "0111"; 		-- connect shiftout to addin
		cond_pilot	<= "010"; 		-- equal 0
		w_en 		 	<= '0';			-- disable the register file
 	when "011000" => --OP_BNEZ (branch not equal zero)
		left_mux  	<= '1';			-- NPC
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0111";	 	-- connect shiftout to addin
		cond_pilot	<= "101"; 		-- not equal 0
		w_en 		 	<= '0';			-- disable the register file
 	when "011001" => --OP_BLTZ (branch less than zero)
		left_mux  	<= '1';			-- NPC
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0111"; 		-- connect shiftout to addin
		cond_pilot	<= "100"; 		-- less 0
		w_en 		 	<= '0';			-- disable the register file
	when "011010" => --OP_BGTZ (branch greater than zero)
		left_mux  	<= '1';			-- NPC
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0111"; 		-- connect shiftout to addin
		cond_pilot	<= "001"; 		-- greater 0
		w_en 			<= '0';			-- disable the register file
	when "011011" => --OP_BLEZ (branch less or equal than zero)
		left_mux  	<= '1';			-- NPC
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0111"; 		-- connect shiftout to addin
		cond_pilot	<= "110"; 		-- less or equal 0
		w_en 		 	<= '0';			-- disable the register file
	when "011100" => --OP_BGEZ (branch greater or equal than zero)
		left_mux  	<= '1';			-- NPC
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0111"; 		-- connect shiftout to addin
		cond_pilot	<= "011"; 		-- greater or equal 0
		w_en 		 	<= '0';			-- disable the register file
	when "011101" => --OP_BEQR (branch equal than register)
		left_mux  	<= '1';			-- NPC
		right_mux 	<= '0';  		-- register B
		alu_mux	 	<= "0001"; 		-- ADD_SUB
		info		 	<= '0';			-- unsigned number
		types	 	 	<= '0';			-- addition
		cond_pilot	<= "010"; 		-- equal register
		w_en 		 	<= '0';			-- disable the register file		
	when "011110" => --OP_BNER (branch not equal than register)
		left_mux  	<= '1';			-- NPC
		right_mux 	<= '0';  		-- register B
		alu_mux	 	<= "0001"; 		-- ADD_SUB
		info		 	<= '0';			-- unsigned number
		types	 	 	<= '0';			-- addition
		cond_pilot	<= "101"; 		-- not equal register
		w_en 		 	<= '0';			-- disable the register file
	when "011111" => --OP_BLTR (branch less than register)
		left_mux  	<= '1';			-- NPC
		right_mux 	<= '0';			-- register B
		alu_mux	 	<= "0001"; 		-- ADD_SUB
		info		 	<= '0';			-- unsigned number
		types		 	<= '0';			-- addition
		cond_pilot	<= "100"; 		-- less register
		w_en 		 	<= '0';			-- disable the register file
	when "100000" => --OP_BGTR (branch greater than register)
		left_mux  	<= '1';			-- NPC
		right_mux 	<= '0';			-- register B
		alu_mux	 	<= "0001"; 		-- ADD_SUB
		info		 	<= '0';			-- unsigned number
		types		 	<= '0';			-- addition
		cond_pilot	<= "001"; 		-- greater register
		w_en 		 	<= '0';			-- disable the register file
	when "100001" => --OP_BLER (branch less or equal than register)
		left_mux  	<= '1';			-- NPC
		right_mux 	<= '0';			-- register B
		alu_mux	 	<= "0001"; 		-- ADD_SUB
		info		 	<= '0';			-- unsigned number
		types 	 	<= '0';			-- addition
		cond_pilot	<= "110"; 		-- less or equal register
		w_en 		 	<= '0';			-- disable the register file
	when "100010" => --OP_BGER (branch greater or equal than register)
		left_mux  	<= '1';			-- NPC
		right_mux 	<= '0';			-- register B
		alu_mux	 	<= "0001";		-- ADD_SUB
		info		 	<= '0';			-- unsigned number
		types		 	<= '0';			-- addition
		cond_pilot	<= "011"; 		-- greater or equal register
		w_en 		 	<= '0';			-- disable the register file
--additional operations
	when "100011" => -- OP_MOVI (move an immediate to a register)
		left_mux		<= '0';			-- register A (not used)
		right_mux	<= '1';			-- immediate 
		alu_mux		<= "1010";		-- MOV
 	when "100100" => -- OP_NANDI ( & not immediate) (new!)
		left_mux 	<= '0';			-- register A
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0100";		-- LOG
		info		 	<= '1';			-- negative operation
		types		 	<= '0';
		extra		 	<= '0';
	when "100101" => -- OP_NORI ( | not immediate) (new!)
		left_mux 	<= '0';			-- register A
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0100";		-- LOG
		info		 	<= '1';			-- negative operation
		types		 	<= '0';
		extra		 	<= '1';
	when "100110" => -- OP_XNORI ( xor not immediate) (new!)
		left_mux 	<= '0';			-- register A
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "0100";		-- LOG
		info		 	<= '1';			-- negative operation
		types		 	<= '1';
		extra		 	<= '0';
	when "100111" => -- OP_MODI
		left_mux 	<= '0';			-- register A
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "1001";		-- MOD
		info		 	<= '1';			-- signed number
	when "101000" => -- OP_MODUI
		left_mux 	<= '0';			-- register A
		right_mux 	<= '1';			-- immediate
		alu_mux	 	<= "1001";		-- MOD
		info		 	<= '0';			-- unsigned number
--	when std_logic_vector(unsigned(41,6)) => 
-----------------------------------------------

	when "000000" => -- OP_NOP
		left_mux 	<= '0';
		right_mux 	<= '0';
		alu_mux 		<= "0000";
		w_en 			<= '0';
		mem_wr		<= '0';
	when others => -- unknown case
		left_mux 	<= '0';
		right_mux 	<= '0';
		alu_mux		<= "0000";
		w_en 			<= '0';
		mem_wr		<= '0';
end case;

end process DECODE;


end Behavioral;
